#ifndef jolfs_buffer_h_
#define jolfs_buffer_h_

#include <boost/cstdint.hpp>
#include <string>
#include <string.h>


namespace jolfs
{

//
// Buffer that is automatically resizeable.
//
class Buffer
{
	public:
		
		//
		// CONSTRUCTORS
		Buffer(boost::uint32_t fsize = 2048, boost::uint32_t advanceWriteSize = 0);
		~Buffer();

		//
		// METHODS
		
		// @return the base pointer of the buffer
		unsigned char* base() const;

		// @return  the read pointer
		unsigned char* rd() const;	
		void rd(boost::uint32_t advance);

		// @return the write pointer
		unsigned char* wr() const;
		void wr(boost::uint32_t advance);
		void wr(unsigned char* pos);

		// close buffer by adding \0 at the end
		void close(); 

		// @return ensure that there will be enough space
		void ensure(boost::uint32_t space);		

		// @return the size (wr - base)
		boost::uint32_t size() const;

		// @return the size (wr - rd)
		boost::uint32_t length() const;
		
		// @return the capacity of the buffer (total Size)
		boost::uint32_t capacity() const;

		// @return the available space in the buffer (totalSize - wr()
		boost::uint32_t available() const;

		// Reset the wr & rd
		void reset();

		void write(unsigned char* d, boost::uint32_t size);		
		void write(const std::string& d);

	private:

		Buffer(Buffer&);
		Buffer& operator=(Buffer&);

		void alloc(boost::uint32_t size);

		// DATA
		boost::uint32_t   totalSize_;

		unsigned char*       base_;
		unsigned char*       rd_;
		unsigned char*       wr_;
};


//------------------------------------------------------
// INLINES
//------------------------------------------------------


inline Buffer::Buffer(boost::uint32_t fsize, boost::uint32_t advanceWriteSize) 
		: totalSize_(0), base_(0), rd_(0), wr_(0)
{
	ensure(fsize);
	wr(advanceWriteSize);
}

//------------------------------------------------------

inline Buffer::~Buffer()
{ 
	delete [] base_;
}

//------------------------------------------------------

inline unsigned char* Buffer::base() const
{
	return base_;
}

//------------------------------------------------------

inline unsigned char* Buffer::rd() const
{
	return rd_;
}

//------------------------------------------------------

inline void Buffer::rd(boost::uint32_t advance)
{
	rd_ += advance;
}

//------------------------------------------------------

inline unsigned char* Buffer::wr() const
{
	return wr_;
}

//------------------------------------------------------

inline void Buffer::wr(boost::uint32_t advance)
{
	wr_ += advance;
}

inline void Buffer::wr(unsigned char* pos)
{
	wr_ = pos;
}

//------------------------------------------------------

inline boost::uint32_t Buffer::size() const  
{
	return wr() - base();
}

//------------------------------------------------------

inline boost::uint32_t Buffer::length() const  
{
	return wr() - rd();
}

//------------------------------------------------------

inline boost::uint32_t Buffer::available() const
{
	return totalSize_ - (wr() - base());
}

//------------------------------------------------------

inline boost::uint32_t Buffer::capacity() const
{
	return totalSize_;
}

//------------------------------------------------------

inline void Buffer::ensure(boost::uint32_t space)
{
	
	if(available() < space) {
		alloc(capacity() + space );
	}
}

//------------------------------------------------------

inline void Buffer::reset()
{
	rd_ = wr_ = base_;
}

//------------------------------------------------------

inline void Buffer::write(unsigned char* d, boost::uint32_t size)
{
	ensure(size + 1);
	memcpy(wr(), d, size); 
	wr(size);
}

//------------------------------------------------------

inline void Buffer::write(const std::string& d)
{
	this->write((unsigned char*) d.c_str(), d.size());
}

//------------------------------------------------------

inline void Buffer::close()
{
	ensure(1);
	*wr_ = '\0';
	wr(1);
}

//------------------------------------------------------

inline void Buffer::alloc(boost::uint32_t nsize)
{
	if(nsize < totalSize_) return;

	// keep pos of wr and rd	
	boost::uint32_t rdPos;
	boost::uint32_t wrPos;
	
	if(base_) {
		rdPos = rd() - base();
		wrPos = wr() - base();
	} else {
		rdPos = 0;
		wrPos = 0;
	}

	// allocate the buffer: we ensure that we add another byte in order to put a null character
	unsigned char* tmp = new unsigned char[nsize + 1];
	tmp[nsize] = '\0';	

	// copy the buffer.
	if(base_) {
		memcpy(tmp, base_, totalSize_);
		delete [] base_;
		base_ = tmp;
	}
	
	base_ = tmp;
	
	// update ptr
	rd_ = base_ + rdPos;
	wr_ = base_ + wrPos;

	totalSize_ = nsize;
}

}

#endif
